describe('Function', function() {
	"use strict";
	describe('bind', function() {
		var actual, expected, testSubject;

		testSubject = {
			push : function(o) {
				this.a.push(o);
			}
		};

		function func() {
			Array.prototype.forEach.call(arguments, function(a) {
						this.push(a);
					}, this);
			return this;
		};

		beforeEach(function() {
					actual = [];
					testSubject.a = [];
				});

		it('binds properly without a context', function() {
					var context;
					testSubject.func = function() {
						context = this;
					}.bind();
					testSubject.func();
					expect(context).toBe(function() {
								return this
							}.call());
				});
		it(
				'binds properly without a context, and still supplies bound arguments',
				function() {
					var a, context;
					testSubject.func = function() {
						a = Array.prototype.slice.call(arguments);
						context = this;
					}.bind(undefined, 1, 2, 3);
					testSubject.func(1, 2, 3);
					expect(a).toEqual([1, 2, 3, 1, 2, 3]);
					expect(context).toBe(function() {
								return this
							}.call());
				});
		it('binds a context properly', function() {
					testSubject.func = func.bind(actual);
					testSubject.func(1, 2, 3);
					expect(actual).toEqual([1, 2, 3]);
					expect(testSubject.a).toEqual([]);
				});
		it('binds a context and supplies bound arguments', function() {
					testSubject.func = func.bind(actual, 1, 2, 3);
					testSubject.func(4, 5, 6);
					expect(actual).toEqual([1, 2, 3, 4, 5, 6]);
					expect(testSubject.a).toEqual([]);
				});

		it('returns properly without binding a context', function() {
					testSubject.func = function() {
						return this;
					}.bind();
					var context = testSubject.func();
					expect(context).toBe(function() {
								return this
							}.call());
				});
		it(
				'returns properly without binding a context, and still supplies bound arguments',
				function() {
					var context;
					testSubject.func = function() {
						context = this;
						return Array.prototype.slice.call(arguments);
					}.bind(undefined, 1, 2, 3);
					actual = testSubject.func(1, 2, 3);
					expect(context).toBe(function() {
								return this
							}.call());
					expect(actual).toEqual([1, 2, 3, 1, 2, 3]);
				});
		it('returns properly while binding a context properly', function() {
					var ret;
					testSubject.func = func.bind(actual);
					ret = testSubject.func(1, 2, 3);
					expect(ret).toBe(actual);
					expect(ret).not.toBe(testSubject);
				});
		it(
				'returns properly while binding a context and supplies bound arguments',
				function() {
					var ret;
					testSubject.func = func.bind(actual, 1, 2, 3);
					ret = testSubject.func(4, 5, 6);
					expect(ret).toBe(actual);
					expect(ret).not.toBe(testSubject);
				});
		it('passes the correct arguments as a constructor', function() {
					var ret, expected = {
						name : "Correct"
					};
					testSubject.func = function(arg) {
						return arg;
					}.bind({
								name : "Incorrect"
							});
					ret = new testSubject.func(expected);
					expect(ret).toBe(expected);
				});
		it(
				'returns the return value of the bound function when called as a constructor',
				function() {
					var oracle = [1, 2, 3];
					var subject = function() {
						return oracle;
					}.bind(null);
					var result = new subject;
					expect(result).toBe(oracle);
				});
		it('returns the correct value if constructor returns primitive',
				function() {
					var oracle = [1, 2, 3];
					var subject = function() {
						return oracle;
					}.bind(null);
					var result = new subject;
					expect(result).toBe(oracle);

					oracle = {};
					result = new subject;
					expect(result).toBe(oracle);

					oracle = function() {
					};
					result = new subject;
					expect(result).toBe(oracle);

					oracle = "asdf";
					result = new subject;
					expect(result).not.toBe(oracle);

					oracle = null;
					result = new subject;
					expect(result).not.toBe(oracle);

					oracle = true;
					result = new subject;
					expect(result).not.toBe(oracle);

					oracle = 1;
					result = new subject;
					expect(result).not.toBe(oracle);
				});
		it(
				'returns the value that instance of original "class" when called as a constructor',
				function() {
					var classA = function(x) {
						this.name = x || "A";
					}
					var classB = classA.bind(null, "B");

					var result = new classB;
					expect(result instanceof classA).toBe(true);
					expect(result instanceof classB).toBe(true);
				});
	});
});
